package com.spzx.product.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.spzx.common.core.exception.ServiceException;
import com.spzx.common.core.utils.bean.BeanUtils;
import com.spzx.product.api.domain.Product;
import com.spzx.product.api.domain.ProductDetails;
import com.spzx.product.api.domain.ProductSku;
import com.spzx.product.api.domain.vo.SkuLockVo;
import com.spzx.product.api.domain.vo.SkuPrice;
import com.spzx.product.api.domain.vo.SkuQuery;
import com.spzx.product.api.domain.vo.SkuStockVo;
import com.spzx.product.domain.SkuStock;
import com.spzx.product.mapper.ProductDetailsMapper;
import com.spzx.product.mapper.ProductMapper;
import com.spzx.product.mapper.ProductSkuMapper;
import com.spzx.product.mapper.SkuStockMapper;
import com.spzx.product.service.IProductService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 商品Service业务层处理
 */
@Slf4j
@Service
@Transactional
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements IProductService {

    @Autowired
    private ProductMapper productMapper;

    @Autowired
    private ProductSkuMapper productSkuMapper;

    @Autowired
    private ProductDetailsMapper productDetailsMapper;

    @Autowired
    private SkuStockMapper skuStockMapper;


    //@Autowired
    //private StringRedisTemplate stringRedisTemplate; //适合 key和value都是字符串类型

    @Autowired
    private RedisTemplate redisTemplate; //适合值是任意类型

    /**
     * 查询商品列表
     *
     * @param product 商品
     * @return 商品
     */
    @Override
    public List<Product> selectProductList(Product product) {
        return productMapper.selectProductList(product);
    }

    //原子性
    @Override
    public int insertProduct(Product product) {
        //1.保存Product对象到product表
        productMapper.insert(product); //主键回填

        //2.保存List<ProductSku>对象到product_sku表
        List<ProductSku> productSkuList = product.getProductSkuList();
        if (CollectionUtils.isEmpty(productSkuList)) {
            throw new ServiceException("SKU数据为空");
        }
        int size = productSkuList.size();
        for (int i = 0; i < size; i++) {
            ProductSku productSku = productSkuList.get(i);
            productSku.setSkuCode(product.getId() + "_" + i);
            productSku.setSkuName(product.getName() + " " + productSku.getSkuSpec());
            productSku.setProductId(product.getId());
            productSkuMapper.insert(productSku);

            //添加商品库存  //3.保存List<SkuStock>对象到sku_stock表
            SkuStock skuStock = new SkuStock();
            skuStock.setSkuId(productSku.getId());
            skuStock.setTotalNum(productSku.getStockNum());
            skuStock.setLockNum(0);
            skuStock.setAvailableNum(productSku.getStockNum());
            skuStock.setSaleNum(0);
            skuStockMapper.insert(skuStock);
        }

        //4.保存ProductDetails对象到product_details表
        ProductDetails productDetails = new ProductDetails();
        productDetails.setImageUrls(String.join(",", product.getDetailsImageUrlList()));
        productDetails.setProductId(product.getId());
        productDetailsMapper.insert(productDetails);

        return 1;
    }


    @Override
    public Product selectProductById(Long id) {
        //1.根据id查询Product对象
        Product product = productMapper.selectById(id);

        //2.封装扩展字段：查询商品对应多个List<ProductSku>
        //select * from product_sku where product_id =?
        List<ProductSku> productSkuList = productSkuMapper.selectList(new LambdaQueryWrapper<ProductSku>().eq(ProductSku::getProductId, id));
        List<Long> productSkuIdList = productSkuList.stream().map(productSku -> productSku.getId()).toList();


        // select * from sku_stock where sku_id in (1,2,3,4,5,6)
        List<SkuStock> skuStockList = skuStockMapper.selectList(new LambdaQueryWrapper<SkuStock>().in(SkuStock::getSkuId, productSkuIdList));

        Map<Long, Integer> skuIdToTatalNumMap = skuStockList.stream().collect(Collectors.toMap(SkuStock::getSkuId, SkuStock::getTotalNum));
        productSkuList.forEach(productSku -> {
            //返回ProductSku对象，携带了库存数据；
            productSku.setStockNum(skuIdToTatalNumMap.get(productSku.getId()));
        });

        product.setProductSkuList(productSkuList);

        //3.封装扩展字段：商品详情图片List<String>
        ProductDetails productDetails = productDetailsMapper.selectOne(new LambdaQueryWrapper<ProductDetails>().eq(ProductDetails::getProductId, id));
        String imageUrls = productDetails.getImageUrls();   //url,url,url
        String[] urls = imageUrls.split(",");
        product.setDetailsImageUrlList(Arrays.asList(urls));
        //返回Product对象
        return product;
    }


    @Override
    public int updateProduct(Product product) {

        //延时双删 保证 数据一致性    最终一致性。
        //修改数据库数据之前，先删除一遍缓存数据
        /*List<ProductSku> productSkuList = product.getProductSkuList();
        List<Long> skuIdList = productSkuList.stream().map(ProductSku::getId).toList();*/
        List<ProductSku> productSkuList = product.getProductSkuList();
        List<Long> skuIdList = productSkuList.stream().map(ProductSku::getId).toList();

        for (Long skuId : skuIdList) {
            String dataKey = "product:sku:" + skuId;
            redisTemplate.delete(dataKey);
        }


        //1.更新Product
        productMapper.updateById(product);

        //2.更新SKU   List<ProductSku>
        if (CollectionUtils.isEmpty(productSkuList)) {
            throw new ServiceException("SKU数据为空");
        }
        productSkuList.forEach(productSku -> {
            productSkuMapper.updateById(productSku);

            //3.更新库存   List<ProductSku> -> 获取扩展字段stockNum
            SkuStock skuStock = skuStockMapper.selectOne(new LambdaQueryWrapper<SkuStock>().eq(SkuStock::getSkuId, productSku.getId()));
            skuStock.setTotalNum(productSku.getStockNum());
            skuStock.setAvailableNum(skuStock.getTotalNum() - skuStock.getLockNum());
            skuStockMapper.updateById(skuStock);
        });

        //4.更新详情ProductDetails
        ProductDetails productDetails = productDetailsMapper.selectOne(new LambdaQueryWrapper<ProductDetails>().eq(ProductDetails::getProductId, product.getId()));
        productDetails.setImageUrls(String.join(",", product.getDetailsImageUrlList()));
        productDetailsMapper.updateById(productDetails);


        //修改数据库后，睡一会再删除一遍缓存。
        try {
            Thread.sleep(500); //保证数据库端主从复制可以全部完成。
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (Long skuId : skuIdList) {
            String dataKey = "product:sku:" + skuId;
            redisTemplate.delete(dataKey);
        }

        return 1;
    }


    /*@Override
    public int updateProduct(Product product) {

        //1.更新Product
        productMapper.updateById(product);

        //2.更新SKU   List<ProductSku>
        List<ProductSku> productSkuList = product.getProductSkuList();
        if (CollectionUtils.isEmpty(productSkuList)) {
            throw new ServiceException("SKU数据为空");
        }
        productSkuList.forEach(productSku -> {
            productSkuMapper.updateById(productSku);

            //3.更新库存   List<ProductSku> -> 获取扩展字段stockNum
            SkuStock skuStock = skuStockMapper.selectOne(new LambdaQueryWrapper<SkuStock>().eq(SkuStock::getSkuId, productSku.getId()));
            skuStock.setTotalNum(productSku.getStockNum());
            skuStock.setAvailableNum(skuStock.getTotalNum() - skuStock.getLockNum());
            skuStockMapper.updateById(skuStock);
        });

        //4.更新详情ProductDetails
        ProductDetails productDetails = productDetailsMapper
                .selectOne(new LambdaQueryWrapper<ProductDetails>().eq(ProductDetails::getProductId, product.getId()));
        productDetails.setImageUrls(String.join(",", product.getDetailsImageUrlList()));
        productDetailsMapper.updateById(productDetails);

        return 1;
    }*/


    @Override
    public int deleteProductByIds(Long[] ids) {
        //1.删除Product表数据
        // delete from product where id in (1,2)
        productMapper.deleteBatchIds(Arrays.asList(ids));

        //2.删除ProductSku表数据
        List<ProductSku> productSkuList = productSkuMapper.selectList(new LambdaQueryWrapper<ProductSku>().in(ProductSku::getProductId, Arrays.asList(ids)));
        List<Long> productSkuIdList = productSkuList.stream().map(ProductSku::getId).toList();
        productSkuMapper.deleteBatchIds(productSkuIdList);

        //3.删除SkuStock表数据
        skuStockMapper.delete(new LambdaQueryWrapper<SkuStock>().in(SkuStock::getSkuId, productSkuIdList));

        //4.删除ProductDetails表数据
        // delete from product_details where product_id in (1,2)
        productDetailsMapper.delete(new LambdaQueryWrapper<ProductDetails>().in(ProductDetails::getProductId, Arrays.asList(ids)));
        return 1;
    }


    @Override
    public void updateAuditStatus(Long id, Integer auditStatus) {
        Product product = new Product();
        product.setId(id);
        if (auditStatus == 1) {
            product.setAuditStatus(1);
            product.setAuditMessage("审批通过");
        } else {
            product.setAuditStatus(-1);
            product.setAuditMessage("审批拒绝");
        }
        productMapper.updateById(product);
    }

    //添加事务保证我们的更新是原子性的
    @Transactional
    @Override
    public void updateStatus(Long id, Integer status) {
        Product product = new Product();
        product.setId(id);
        if (status == 1) {
            //现在是只进行了商品的上架，但是没有进行商品对应的下边的sku没有上架更新
            //进行商品下的sku进行更新,update需要一个product的实体类
            product.setStatus(1);

            ProductSku productSku = new ProductSku();
            productSku.setStatus(1);
            productSkuMapper.update(productSku, new LambdaUpdateWrapper<ProductSku>().eq(ProductSku::getProductId, id));
            /*//这个实体类是lambdaUpdate中的实体类
            ProductSku productSku = new ProductSku();
            //将当前更新的product的状态也同步更新到相应的sku中去
            productSku.setStatus(1);
            productSkuMapper.update(productSku,new LambdaUpdateWrapper<ProductSku>().eq(ProductSku::getProductId,id));*/


            //如果说一个商品进行上架后，他的sku也需要加到redis的bitmap中
            List<ProductSku> ProductSkuList = productSkuMapper.selectList(new LambdaQueryWrapper<ProductSku>().eq(ProductSku::getProductId, id).eq(ProductSku::getStatus, 1));
            if (!CollectionUtils.isEmpty(ProductSkuList)) {
                String skuKey = "sku:product:data";
                /*for (ProductSku productSku : ProductSkuList) {
                    Long skuId = productSku.getId();
                    redisTemplate.opsForValue().setBit(skuKey,skuId,true);
                }*/
                for (ProductSku sku : ProductSkuList) {
                    Long skuId = sku.getId();
                    redisTemplate.opsForValue().setBit(skuKey, skuId, true);
                }
            }
        } else {

            //当商品下架的时候，我们也需要将所有的商品的sku的状态做修改，且商品的状态为-1
            List<ProductSku> ProductSkuList = productSkuMapper.selectList(new LambdaQueryWrapper<ProductSku>().eq(ProductSku::getProductId, id).eq(ProductSku::getStatus, 1));

            if (CollectionUtils.isEmpty(ProductSkuList)) {
                String skuKey = "sku:product:data";
                for (ProductSku prodcutSku : ProductSkuList) {
                    Long skuId = prodcutSku.getId();
                    redisTemplate.opsForValue().setBit(skuKey, skuId, false);
                    String dataKey = "product:sku:" + skuId;
                    redisTemplate.delete(dataKey);
                }
            }
            product.setStatus(-1);
        }
        productMapper.updateById(product);
    }


    @Override
    public List<ProductSku> getTopSale() {
        return productSkuMapper.getTopSale();
    }


    @Override
    public List<ProductSku> skuList(SkuQuery skuQuery) {
        return productSkuMapper.skuList(skuQuery);
    }


    /**
     * 服务提供者：6个接口来服务于商品详情查询。需要进行优化，提供查询效率。
     * 需要使用redis来提高性能。
     */

    /**
     * 根据skuId查询这个sku的详细信息
     *
     * @param skuId
     * @return
     */
    @Override
    public ProductSku getProductSku(Long skuId) {
        try {
            //1.首先从缓存中去获取数据
            //1.1构建数据的key
            //在缓存中，查看有没有某一个数据的缓存
            // 因为在缓存中的所有数据，都是以k，v的形式存在的，所以使用当前传入的skuId来构建一个属于自己的key
            //在缓存只认这个skuId，数据库中是根据ID进行查询对应的数据，但是在redis缓存中，就得用id相应的key来获取value了
            // 然后，再从缓存里取出相应的数据，查看是否为空，进行y/n的编写
            String dataKey = "product:sku:" + skuId;
            //1.2从缓存中取数据
            ProductSku productSku = (ProductSku) redisTemplate.opsForValue().get(dataKey);
            if (productSku != null) {
                System.out.println("取到数据了");
                //从内存中取到数据了就返回
                return productSku;
            }
            //2.如果缓存中没有的话，去数据库中查询,并且要把查询的这个null值进行缓存，写入到redis中
            //2.1获取到分布式锁，获取数据库信息，防止穿透
            String LockKey = "product:sku:lock" + dataKey;
            //2.2设置uuid设置每个线程自己有的锁
            String LockVal = UUID.randomUUID().toString().replaceAll("-", "");
            //2.3尝试获取锁
            Boolean flag = redisTemplate.opsForValue().setIfAbsent(LockKey, LockVal, 5, TimeUnit.SECONDS);
            if (flag) {//2.4成功获取到锁了
                try {
                    //获取到锁了就先到数据库中进行查询
                    productSku = getProductSkuFromDB(skuId);
                    //设置缓存时间
                    int i = productSku == null ? 1 * 60 : 10 * 60;
                    //将数据写入到redis缓存中
                    redisTemplate.opsForValue().set(dataKey, productSku, i, TimeUnit.SECONDS);
                    return productSku;
                } finally {
                    //释放锁是必然的喽
                    //为了保证释放锁的时候，释放的是自己的锁，使用lua脚本，保证释放锁的原子性
                    String scriptText = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" + "then\n" + "    return redis.call(\"del\",KEYS[1])\n" + "else\n" + "    return 0\n" + "end";
                    DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
                    redisScript.setScriptText(scriptText);
                    redisScript.setResultType(Long.class);
                    redisTemplate.execute(redisScript, Arrays.asList(LockKey), LockVal);
                }
            } else {//如果没有获取到锁的话，就进行自旋
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return this.getProductSkuFromDB(skuId);
            }
        } catch (Exception e) {
            //进行兜底方案
            return this.getProductSkuFromDB(skuId);
        }
    }

    public ProductSku getProductSkuFromDB(Long skuId) {
        return productSkuMapper.selectById(skuId);
    }


    @Override
    public Product getProduct(Long id) {
        return productMapper.selectById(id);
    }


    @Override
    public SkuPrice getSkuPrice(Long skuId) {
        ProductSku productSku = productSkuMapper.selectOne(new LambdaQueryWrapper<ProductSku>().eq(ProductSku::getId, skuId).select(ProductSku::getSalePrice, ProductSku::getMarketPrice));
        SkuPrice skuPrice = new SkuPrice();
        BeanUtils.copyProperties(productSku, skuPrice);
        return skuPrice;
    }


    @Override
    public ProductDetails getProductDetails(Long id) {
        return productDetailsMapper.selectOne(new LambdaQueryWrapper<ProductDetails>().eq(ProductDetails::getProductId, id));
    }

    /**
     * "skuSpecValueMap": {
     * "黑色 + 18G": 6,
     * "红色 + 18G": 4,
     * "白色 + 8G": 1,
     * "白色 + 18G": 2,
     * "黑色 + 8G": 5,
     * "红色 + 8G": 3
     * }
     *
     * @param id productId
     * @return
     */
    @Override
    public Map<String, Long> getSkuSpecValue(Long id) {
        List<ProductSku> productSkuList = productSkuMapper.selectList(new LambdaQueryWrapper<ProductSku>().eq(ProductSku::getProductId, id).select(ProductSku::getId, ProductSku::getSkuSpec));
        Map<String, Long> skuSpecValueMap = new HashMap<>();
        productSkuList.forEach(item -> {
            skuSpecValueMap.put(item.getSkuSpec(), item.getId());
        });
        return skuSpecValueMap;
    }


    @Override
    public SkuStockVo getSkuStock(Long skuId) {
        SkuStock skuStock = skuStockMapper.selectOne(new LambdaQueryWrapper<SkuStock>().eq(SkuStock::getSkuId, skuId));
        SkuStockVo skuStockVo = new SkuStockVo();
        BeanUtils.copyProperties(skuStock, skuStockVo);
        return skuStockVo;
    }


    // select * from product_sku where id in (1,2,3)
    // select id,sale_price,market_price from product_sku where id in (1,2,3)

    /**
     * 获取到商品的实时价格
     *
     * @param skuIdList
     * @return
     */
    @Override
    public List<SkuPrice> getSkuPriceList(List<Long> skuIdList) {
        /*if (CollectionUtils.isEmpty(skuIdList)) {
            return new ArrayList<SkuPrice>();
        }
        List<ProductSku> skuList = productSkuMapper
                .selectList(new LambdaQueryWrapper<ProductSku>().in(ProductSku::getId, skuIdList)
                        .select(ProductSku::getId, ProductSku::getSalePrice, ProductSku::getMarketPrice));
        if (CollectionUtils.isEmpty(skuList)) {
            return new ArrayList<SkuPrice>();
        }
        return skuList.stream().map((sku) -> {
            SkuPrice skuPrice = new SkuPrice();
            skuPrice.setSkuId(sku.getId());
            skuPrice.setSalePrice(sku.getSalePrice());
            skuPrice.setMarketPrice(sku.getMarketPrice());
            return skuPrice;
        }).toList();
    }*/
        LambdaQueryWrapper<ProductSku> psq = new LambdaQueryWrapper<>();
        LambdaQueryWrapper<ProductSku> select = psq.in(ProductSku::getId, skuIdList).select(ProductSku::getId, ProductSku::getSalePrice);
        List<ProductSku> skuList = productSkuMapper.selectList(select);

        return skuList.stream().map(skuprice -> {
            SkuPrice skuPrice = new SkuPrice();
            skuPrice.setSkuId(skuprice.getId());
            skuPrice.setSalePrice(skuprice.getSalePrice());
            return skuPrice;
        }).collect(Collectors.toList());
    }

    /**
     * 检查库存与锁定库存
     *
     * @param orderNo       订单编号
     * @param skuLockVoList
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public String checkAndLock(String orderNo, List<SkuLockVo> skuLockVoList) {
        //定义锁库存的key
        String key = "sku:checkAndLock:" + orderNo;

        //定义存储到redis中数据的key
        String dataKey = "sku:lock:data:" + orderNo; //业务完成，需要将锁定的数据保存到redis中时用到的key

        //0.远程调用的重试
        //返回true，上锁成功
        //返回false，上锁失败
        Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent(key, orderNo, 1, TimeUnit.HOURS);
        if(!ifAbsent){//
            if(redisTemplate.hasKey(dataKey)){
                return "";
            }else{
                return "已经锁定库存无法重试";
            }
        }
        //1.查看库存够不够
        if(CollectionUtils.isEmpty(skuLockVoList)){
            //是空的
            return "参数为空";
        }
        //集合不是空的，那么就对集合中的sku集合在数据库中的库存查出来
        StringBuilder builder = new StringBuilder("");
        for (SkuLockVo skuLockVo : skuLockVoList) {
            //将skuId对应的库存进行查询
            SkuStock skuStock = skuStockMapper.check(skuLockVo.getSkuId(), skuLockVo.getSkuNum());
            //返回结果是sql语句的影响行数，如果想要买的数量大于库存中的数量的话，查询结果影响行数就是null
            if(skuStock==null){
                //库存不足，告诉用户是哪一件商品的库存不足
                builder.append("商品"+skuLockVo.getSkuId()+"库存不够");
            }
        }
        //2.库存不够的话，锁库存失败
        if(builder.length() == 0){
            //这里删除的是check时候的for update行锁
            redisTemplate.delete(key);
            return builder.toString();//正常返回，行锁自动释放
        }
        //3.库存足够，锁定成功
        //遍历每个商品的库存等等
        for (SkuLockVo skuLockVo : skuLockVoList) {
            //获取商品检查影响的行数
            int lockCount = skuStockMapper.lock(skuLockVo.getSkuId(), skuLockVo.getSkuNum());
            if(lockCount==0){//
                redisTemplate.delete(key);
                throw new ServiceException("锁库存失败");
            }
            skuLockVo.setIsHaveStock(true);
        }
        //4.将数据存储到redis缓存中
        redisTemplate.opsForValue().set(dataKey,skuLockVoList);//不用设置过期时间

        return "";

    }
}